import random, os, sys
import hashlib, time
import pickle
import marshal, struct
import ctypes


class trackHashes():
    def __init__(self, path):
        self.path = path
        self.Format = "=Q20s"
        for i in [0,1,2,3,4,5,6,7,8,9,"a","b","c","d","e","f"]:
            if os.path.isfile("%s/%s"%(self.path, str(i))) == False:
                t = open("%s/%s"%(self.path, str(i)), "w")
                t.close()

    def reHash(self):
        for root, dirs, files in os.walk(self.path):
            for f in files:
                if os.path.getsize("%s/%s"%(root, f))/28 > 150:
                    for i in [0,1,2,3,4,5,6,7,8,9,"a","b","c","d","e","f"]:
                        t = open("%s/%s%s"%(self.path, f, str(i)), "w")
                        t.close()
                    fp = open("%s/%s"%(root, f), "rb")
                    while True:
                        tmp = fp.read(struct.calcsize(self.Format))
                        if len(tmp) == 28:
                            c, ch = struct.unpack(self.Format, tmp)
                            new = ""
                            for i in range(0,len(ch)):
                                new += "%x"%ord(ch[i:i+1])
                            nfp = open(self.getHashFile(new.zfill(40)), "r+b")
                            nfp.seek(0, os.SEEK_END)
                            nfp.write(struct.pack(self.Format, c, ch))
                            nfp.close()
                        else:
                            break
                    fp.close()
                    fp = open("%s/%s"%(root, f), "w")
                    fp.close()

    def getHashFile(self, h):
        for i in [1,2,3,4,5,6,7]:
            if os.path.isfile("%s/%s"%(self.path, h[:i])) == False:
                break
        return "%s/%s"%(self.path, h[:i-1])

    def hashTOstr(self, h):
        arr = []
        n = int(h, 16)
        while n:
            n, rem = divmod(n, 256)
            arr.append(rem)
        while len(arr) < len(h)/2:
            arr.append(0)
        b = bytearray(reversed(arr))
        return str(b)
    def strTOhash(self, s):
        new = ""
        for i in range(0,len(s)):
            t = "%x"%ord(s[i:i+1])
            t = t.zfill(2)
            new += t
        return new


    def getHashpos(self, h):
        fp = open(self.getHashFile(h), "rb")
        fp.seek(0)
        while True:
            tmp = fp.read(struct.calcsize(self.Format))
            if len(tmp) == struct.calcsize(self.Format):
                c, ch = struct.unpack(self.Format, tmp)
                if ch == self.hashTOstr(h):
                    pos = fp.tell()-struct.calcsize(self.Format)
                    fp.close()
                    return pos
            else:
                break
        fp.close()
        return False

    def getHashcount(self, h):
        fp = open(self.getHashFile(h), "rb")
        fp.seek(0)
        while True:
            tmp = fp.read(struct.calcsize(self.Format))
            if len(tmp) == struct.calcsize(self.Format):
                c, ch = struct.unpack(self.Format, tmp)
                new = ""
                for i in range(0,len(ch)):
                    new += "%x"%ord(ch[i:i+1])
                new = new.zfill(40)
                if ch == self.hashTOstr(h):
                    fp.close()
                    return c
            else:
                break
        fp.close()
        return 0

    def addHash(self, h):
        t = int(round(time.time() * 1000))
        fp = open(self.getHashFile(h), "r+b")
        pos = self.getHashpos(h)
        if type(pos) is not bool:
            c = self.getHashcount(h)
            fp.seek(pos)
            fp.write(struct.pack("=Q", c+1))
        else:
            b = self.hashTOstr(h)
            fp.seek(0, os.SEEK_END)
            fp.write(struct.pack(self.Format, 1, b))
        fp.close()
        if int(round(time.time() * 1000)) - t > 5:
            print "REHASHING"
            self.reHash()
        return True

    def delHash(self, h):
        t = int(round(time.time() * 1000))
        fp = open(self.getHashFile(h), "r+b")
        pos = self.getHashpos(h)
        if type(pos) is not bool:
            c = self.getHashcount(h)-1
            if c < 0:
                c = 0
            fp.seek(pos)
            fp.write(struct.pack("=Q", c))
        return True




Hashes = trackHashes("/mnt/tmpfs/")

fp = open("/dev/urandom", "r")
cc = 0
t = int(round(time.time()))
#for i in range(0,500000):
while True:
    c = ""
#    for i in range(0,20):
#        b = struct.unpack("=B",fp.read(1))[0]
#        c += "%x"%(b)
#    c = c.zfill(40)
#    print c
    c = hashlib.sha1(fp.read(1024)).hexdigest()

    Hashes.addHash(c)
    if Hashes.getHashcount(c) != 1:
        print "ERROR 0", Hashes.getHashcount(c), c, cc
        sys.exit(1)
    Hashes.delHash(c)
    if Hashes.getHashcount(c) != 0:
        print "ERROR 1", Hashes.getHashcount(c), c
        sys.exit(1)
    Hashes.addHash(c)
    if Hashes.getHashcount(c) != 1:
        print "ERROR 2", Hashes.getHashcount(c), c, cc
        sys.exit(1)
    Hashes.addHash(c)
    if Hashes.getHashcount(c) != 2:
        print "ERROR 3", Hashes.getHashcount(c), c, cc
        sys.exit(1)
    Hashes.addHash(c)
    if Hashes.getHashcount(c) != 3:
        print "ERROR 4", Hashes.getHashcount(c), c, cc
        sys.exit(1)
    cc = cc+1
    if cc%1000 == 0:
        print cc
        print int(round(time.time())) - t
        t = int(round(time.time()))




